/*
 * Copyright (c) 2010 Mathias Garbe <code@mathias-garbe.de>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef IRC_HPP
#define IRC_HPP

#include <irc.h>
#include <IrcSession>
#include <IrcBuffer>

#include <QTimer>

#include <iostream>
#include "Helper.hpp"

class MainWindow;

namespace IrcClient
{
    class Session : public Irc::Session
    {
        Q_OBJECT

    public:
        MainWindow *mainWin;

        Session(MainWindow *mainWin, QObject* parent = 0)
            : Irc::Session(parent)
        {
            this->mainWin = mainWin;

            connect(this, SIGNAL(connected()), SLOT(on_connected()));
            connect(this, SIGNAL(disconnected()), SLOT(on_disconnected()));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "-!- IRC started..."));
        }

    protected Q_SLOTS:
        void on_connected()
        {
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "-!- <b>Connected to irc.freenode.net</b>"));
        }

        void on_disconnected()
        {
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "-!- <b>Disconnected from IRC</b>"));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnDisconnect", Qt::QueuedConnection);
        }



        void on_bufferAdded(Irc::Buffer* buffer)
        {

        }

        void on_bufferRemoved(Irc::Buffer* buffer)
        {

        }

    protected:
        virtual Irc::Buffer* createBuffer(const QString& receiver);
    };

    class Buffer : public Irc::Buffer
    {
        Q_OBJECT

    private:
        MainWindow *mainWin;
        Session *session;

        bool joined;


    public:
        Buffer(const QString& receiver, Irc::Session* parent)
            : Irc::Buffer(receiver, parent)
        {
            this->session = (Session*)parent;
            this->mainWin = session->mainWin;
            this->joined = false;

            connect(this, SIGNAL(joined(QString)), SLOT(on_joined(QString)));
            connect(this, SIGNAL(parted(QString, QString)), SLOT(on_parted(QString, QString)));
            connect(this, SIGNAL(quit(QString, QString)), SLOT(on_quit(QString, QString)));
            connect(this, SIGNAL(nickChanged(QString, QString)), SLOT(on_nickChanged(QString, QString)));
            connect(this, SIGNAL(modeChanged(QString, QString, QString)), SLOT(on_modeChanged(QString, QString, QString)));
            connect(this, SIGNAL(topicChanged(QString, QString)), SLOT(on_topicChanged(QString, QString)));
            connect(this, SIGNAL(invited(QString, QString, QString)), SLOT(on_invited(QString, QString, QString)));
            connect(this, SIGNAL(kicked(QString, QString, QString)), SLOT(on_kicked(QString, QString, QString)));
            connect(this, SIGNAL(messageReceived(QString, QString, Irc::Buffer::MessageFlags)),
                SLOT(on_messageReceived(QString, QString, Irc::Buffer::MessageFlags)));
            connect(this, SIGNAL(motdReceived(QString)), SLOT(on_motd(QString)));
            connect(this, SIGNAL(noticeReceived(QString, QString, Irc::Buffer::MessageFlags)), SLOT(on_notice(QString, QString, Irc::Buffer::MessageFlags)));
            connect(this, SIGNAL(numericMessageReceived(QString, uint, QStringList)), SLOT(on_numericMessageReceived(QString, uint, QStringList)));
            connect(this, SIGNAL(unknownMessageReceived(QString, QStringList)), SLOT(on_unknownMessageReceived(QString, QStringList)));
            connect(this, SIGNAL(ctcpActionReceived(QString, QString, Irc::Buffer::MessageFlags)), SLOT(on_ctcpActionReceived(QString, QString, Irc::Buffer::MessageFlags)));
        }

    protected Q_SLOTS:


        void sendNicks()
        {
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
            QTimer::singleShot(1500, this, SLOT(sendNicks()));
        }

        void sendTopic()
        {
            /*QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnTopic", Qt::QueuedConnection, Q_ARG(QString, this->topic()));
            QTimer::singleShot(1500, this, SLOT(sendTopic()));*/
        }

        void on_joined(const QString& origin)
        {
            QString tmp;
            tmp.append("-!- <b>").append(origin).append("</b> has joined <b>").append(this->receiver()).append("</b>");
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));

            this->joined = true;

            QTimer::singleShot(500, this, SLOT(sendNicks()));
            //QTimer::singleShot(500, this, SLOT(sendTopic()));
        }

        void on_parted(const QString& origin, const QString& message)
        {
            QString tmp;
            tmp.append("-!- ").append(origin).append(" has left <b>").append(this->receiver()).append("</b> [").append(Qt::stripHtml(message)).append("]");
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
        }

        void on_quit(const QString& origin, const QString& message)
        {
            QString tmp;
            tmp.append("-!- ").append(origin).append(" has quit [").append(Qt::stripHtml(message)).append("]");
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
        }

        void on_nickChanged(const QString& origin, const QString& nick)
        {
            QString tmp;
            tmp.append("-!- ").append(origin).append(" is now known as <b>").append(nick).append("</b>");
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
        }

        void on_modeChanged(const QString& origin, const QString& mode, const QString& args)
        {
            QString tmp;
            tmp.append("-").append(origin).append("-  ").append(mode).append(" ").append(args);
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
        }

        void on_topicChanged(const QString& origin, const QString& topic)
        {
            QString tmp;
            tmp.append("-!- ").append(origin).append(" changed the topic to ").append(Qt::stripHtml(topic));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnTopic", Qt::QueuedConnection, Q_ARG(QString, Qt::stripHtml(topic)));
        }

        void on_invited(const QString& origin, const QString& receiver, const QString& channel)
        {

            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
        }

        void on_kicked(const QString& origin, const QString& nick, const QString& message)
        {
            QString tmp;
            tmp.append("-!- ").append(nick).append(" was kicked by <b>").append(origin).append("</b> [").append(Qt::stripHtml(message)).append("]");
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
        }

        void on_messageReceived(const QString& origin, const QString& message, Irc::Buffer::MessageFlags flags)
        {
            if(!this->joined && this->receiver().toStdString() == "#cncnet")
            {
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "Can not send message, not yet joinend the channel. Please wait."));
                return;
            }

            QString newOrigin = origin;
            if(this->session->nick() == origin)
            {
                newOrigin.prepend("<b>");
                newOrigin.append("</b>");
            }

            QString tmp;
            tmp.append("<font style=\"color:#7C7C7C\">&lt;</font>").append(newOrigin).append("<font style=\"color:#7C7C7C\">&gt;</font> ").append(Qt::stripHtml(message));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNicklist", Qt::QueuedConnection, Q_ARG(QStringList, this->names()));
        }

        void on_motd(const QString& motd)
        {
            QString tmp;
            tmp.append("-!- ").append(Qt::stripHtml(motd));
            //QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
        }

        void on_notice(const QString &origin, const QString &notice, Irc::Buffer::MessageFlags flags)
        {
            QString tmp;
            tmp.append("-").append(origin).append("- ").append(notice);
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
        }

        void on_numericMessageReceived(const QString &origin, uint code, const QStringList &params)
        {
            //std::cout << "numeric " << origin.toStdString() << " "  << code << std::endl;
            if(code == Irc::Rfc::ERR_BANNEDFROMCHAN)
            {
                QString tmp;
                tmp.append("-").append(origin).append("- You are banned from this channel.");
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircDisconnect", Qt::QueuedConnection);
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "-!- IRC has been disabled. If you feel you were banned wrongfully, please contact us on the forums."));
            }

            if(code == Irc::Rfc::ERR_NICKCOLLISION)
            {
                QString tmp;
                tmp.append("-").append(origin).append("- Nick collision.");
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircDisconnect", Qt::QueuedConnection);
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "-!- IRC has been disabled. Please change your nick in the settings"));
            }

            if(code == Irc::Rfc::ERR_NICKNAMEINUSE)
            {
                QString tmp;
                tmp.append("-").append(origin).append("- This nickname is already in use!");
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircDisconnect", Qt::QueuedConnection);
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "-!- IRC has been disabled. Please change your nick in the settings"));
            }

            if(code == Irc::Rfc::ERR_RESTRICTED)
            {
                QString tmp;
                tmp.append("-").append(origin).append("- Your connection is restricted!");
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircDisconnect", Qt::QueuedConnection);
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "-!- IRC has been disabled. "));
            }

            if(code == Irc::Rfc::RPL_TOPIC)
            {
                if(params.size() >= 3)
                {
                    QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnTopic", Qt::QueuedConnection, Q_ARG(QString, Qt::stripHtml(params[2])));
                }
            }

            
        }

        void on_unknownMessageReceived(const QString &origin, const QStringList &params)
        {
            //std::cout << "unknown " << origin.toStdString() << std::endl;
        }

        void on_ctcpActionReceived(const QString& origin, const QString& action, Irc::Buffer::MessageFlags flags)
        {
            if(!this->joined && this->receiver().toStdString() == "#cncnet")
            {
                QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, "Can not send message, not yet joinend the channel. Please wait."));
                return;
            }

            QString tmp;
            tmp.append("<b>* ").append(origin).append("</b> ").append(Qt::stripHtml(action));
            QMetaObject::invokeMethod((QObject*)this->mainWin, "ircOnNewChatline", Qt::QueuedConnection, Q_ARG(QString, tmp));
        }
    };
}

#endif